<template>
  <div class="lg:w-1/2 mx-auto h-75vh overflow-auto">
    <ul v-if="!hasAxureDoc" class="w-max mx-auto space-y-3 list-none text-gray-200">
      <!-- <a-button type="primary" @click="pipeline">Fetch</a-button> -->
      <li
        v-for="(text, index) in statusQueue"
        :key="text"
        class="text-3xl animate-fade-in-up animate-duration-200 transition-colors duration-500"
        :class="{ 'text-#4a4a4a': index !== statusQueue.length - 1 }"
      >
        {{ text }}
      </li>
    </ul>
    <div v-else>
      <a-tree
        v-model:checkedKeys="checkedKeys"
        class="box-border px-8 py-4 bg-inherit"
        draggable
        checkable
        block-node
        :check-strictly="true"
        :show-line="false"
        :show-icon="false"
        :default-expand-all="true"
        :virtual="false"
        :tree-data="treeData"
        @drop="onDrop"
        @check="onCheck"
        @select="onSelect"
      >
        <template #title="{ title, dataRef }">
          <div class="flex items-center space-x-3">
            <span v-if="!(dataRef as TransformItem).renaming">{{ title }}</span>
            <template v-else>
              <a-input
                v-model:value="dataRef.title"
                size="small"
                class="w-1/3"
                @press-enter="dataRef.renaming = false"
              ></a-input>
              <i
                class="i-mdi:check-circle ml-1! text-xl text-green-400 transform transition-all duration-400 hover:scale-120"
                title="完成"
                data-flag="rename-done"
              ></i>
            </template>
            <i
              v-show="!dataRef.renaming"
              class="i-mdi:rename text-lg transform transition-all duration-400 hover:scale-120"
              title="重命名"
              data-flag="rename"
            ></i>
            <i
              class="i-mdi:folder-plus text-lg text-[#1668dc] transform transition-all duration-400 hover:scale-120"
              title="新增"
              data-flag="add"
            ></i>
            <i
              class="i-mdi:folder-cancel text-lg text-red-500 transform transition-all duration-400 hover:scale-120"
              title="删除"
              data-flag="remove"
            ></i>
            <!-- <span>{{ JSON.stringify(data) }}</span> -->
          </div>
        </template>

        <template #switcherIcon>
          <span class="i-mdi:folder-arrow-up-down text-[1.25rem] align-middle mr-2"></span>
        </template>
      </a-tree>

      <a-affix :offset-bottom="0" class="h-12">
        <a-button
          type="primary"
          :loading="loading"
          class="create-menu bg-transparent backdrop-blur-xl duration-500 h-full w-full tracking-widest text-xl border-none! outline-none!"
          @click="stepToNext"
        >
          创建菜单
        </a-button>
      </a-affix>
    </div>

    <a-modal
      v-model:open="open"
      :closable="false"
      :mask-closable="false"
      :footer="null"
      centered
      :width="modalWidth"
      wrap-class-name="aaa"
      @cancel="pipeline('')"
    >
      <transition name="fade" mode="out-in">
        <div v-if="isQuering">
          <span
            v-show="hasRecords"
            class="i-mdi-loading animate-spin animate-fade-in-right text-2xl align-text-top"
          ></span>
          <span class="ml-3 text-lg">{{
            hasRecords ? '正在查询历史记录' : '未查询到历史数据, 开始爬取...'
          }}</span>
        </div>
        <div v-else-if="existFileList.length">
          <ul class="space-y-6 p-3">
            <li
              v-for="fileConf in existFileList"
              :key="fileConf.id"
              class="record bg-[#151515] rounded-xl flex items-center text-3xl px-10 cursor-pointer"
              :class="['space-x-8 py-6', !fileConf.id && 'justify-center space-x-2! py-2!']"
              @click="onSelectFile(fileConf.id)"
            >
              <span
                v-if="fileConf.id"
                class="space-x-0.5 flex items-center select-none bg-blue-700 rounded-lg px-2 py-1"
              >
                <span class="font-mono align-text-top pb-1">{</span>
                <span class="i-tabler:json text-4xl leading-1 align-bottom"></span>
                <span class="font-mono pb-1">}</span>
              </span>
              <span v-else class="flex items-center text-gray-400">
                <span class="i-tabler:arrow-big-left-lines-filled transform -rotate-90"></span>
              </span>
              <span class="text-2xl pt-1.25" :class="{ 'text-gray-400': !fileConf.id }">
                {{ fileConf.id ? '选择' : '' }}{{ fileConf.name }}
              </span>
            </li>
          </ul>
        </div>
      </transition>
    </a-modal>
  </div>
</template>

<script setup lang="ts" name="ITree">
import type {
  AntTreeNodeDropEvent,
  AntTreeNodeCheckedEvent,
  AntTreeNodeSelectedEvent
} from 'ant-design-vue/es/tree'
import { message } from 'ant-design-vue'
import useAxureDoc from '@/hooks/useAxureDoc'
import { useGlobalStore } from '@/store'
import useState from '@/hooks/useState'
import { usePathExist, useWriteFile } from '@/hooks/useFile'
import useMinWaitPromise, { useDelay } from '@/hooks/useMinWaitPromise'
import { baseWalk } from '@/utils/tool'

const emit = defineEmits<{
  (event: 'processing'): void
  (event: 'done'): void
}>()

type RemoteTree = TreeItem[]

const transformRemoteTree = (tree: RemoteTree, callback: (item: TransformItem) => void) => {
  const treeTransformed: TransformItem[] = []

  if (Array.isArray(tree)) {
    tree.forEach((item) => {
      const transformItem: TransformItem = {
        title: item.pageName,
        key: item.id || item.pageName,
        noKey: !item.id,
        url: item.url,
        checked: !!item.id
      }
      callback(transformItem)

      if (item.children) {
        transformItem.children = transformRemoteTree(item.children, callback)
      }

      treeTransformed.push(transformItem)
    })
  }

  return treeTransformed
}

// 改
const onSelect = (_: any, e: AntTreeNodeSelectedEvent) => {
  const { nativeEvent, node } = e
  const { target } = nativeEvent

  const flag = (target as HTMLElement).dataset.flag
  const targetNode = node.dataRef as TransformItem

  // prettier-ignore
  switch (flag) {
    case 'add': onAddItem(targetNode); break
    case 'remove': onRemoveItem(node.key as string, node.parent?.node); break
    case 'rename': targetNode.renaming = true; break
    case 'rename-done': targetNode.renaming = false; break
    default: break;
  }

  treeData.value = [...treeData.value]
}

const createItem = (title: string) => {
  return {
    title,
    key: Math.random().toString(),
    noKey: false,
    url: '',
    renaming: true,
    checked: true
  }
}
// 给节点添加一个子项
const onAddItem = (target: TransformItem) => {
  console.log(target)

  const newItem = createItem('')
  target.children = [newItem, ...(target.children ?? [])]
  checkedKeys.value.checked.push(newItem.key)
}

const onCheck = (_: any, e: AntTreeNodeCheckedEvent) => {
  // prettier-ignore
  const { checked, node: { dataRef } } = e
  ;(dataRef as TransformItem).checked = !!checked
}

const onDrop = (info: AntTreeNodeDropEvent) => {
  const dropKey = info.node.key
  const dragKey = info.dragNode.key

  if (info.node.pos) {
    const dropPos = info.node.pos.split('-')
    const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])

    const tranverse = (
      data: TransformItem[],
      key: string | number,
      callback: (item: TransformItem, index: number, parent: TransformItem[]) => void
    ) => {
      data.forEach((item, index) => {
        if (item.key === key) {
          return callback(item, index, data)
        }
        if (item.children) {
          return tranverse(item.children, key, callback)
        }
      })
    }

    const data = [...treeData.value]

    // 查找当前被拖拽的元素，并从其父节点数组中将其删除
    let target!: TransformItem

    tranverse(data, dragKey, (item, index, parent) => {
      parent.splice(index, 1)
      target = item
    })

    console.log(dropPosition, info, info.node.pos, target)

    // dropToGap => 放置的位置是否是两兄弟节点之间, 否则是某元素的子节点
    // dropPosition => 移动的位置位于当前数组的下标
    if (!info.dropToGap) {
      // 移动为某元素的子节点
      tranverse(data, dropKey, (item) => {
        item.children = item.children || []
        item.children.unshift(target)
      })
    } else if (
      // 放置某元素之间
      (info.node.children || []).length > 0 && // 放置位置的元素包含子节点
      info.node.expanded && // 展开状态
      dropPosition === 1 // 放在目标元素的下半部分 => 视为目标元素的子元素
    ) {
      // 如果放置在节点之上，则添加到元素列表的头部
      tranverse(data, dropKey, (item) => {
        item.children = item.children || []
        item.children.unshift(target)
      })
    } else {
      // 移动为目标位置元素的兄弟节点
      let dropNodeParent: TransformItem[] = []
      let dropNodeIndex = 0
      // 获取目标位置元素的父元素列表和目标元素的下标
      tranverse(data, dropKey, (_, index, parent) => {
        dropNodeParent = parent
        dropNodeIndex = index
      })

      if (dropPosition === -1) {
        // 位于目标元素的上半部分
        dropNodeParent.splice(dropNodeIndex, 0, target)
      } else {
        dropNodeParent.splice(dropNodeIndex + 1, 0, target)
      }
    }

    treeData.value = data
  }
}
// 这里不能直接删除指定位置的元素，因为当元素被移动后,
// 对应的位置 key => e.g: '0-1-0' 是不会变的，应该直接从父元素的children中查找
const onRemoveItem = (targetKey: string, parent: TransformItem) => {
  const host = parent ? parent.children : treeData.value
  if (host) {
    const position = host.findIndex((child) => child.key === targetKey)
    const [{ key, checked }] = host.splice(position, 1)

    // 将其从选中列表中删除
    if (checked && key) {
      const removedIndex = checkedKeys.value.checked.findIndex((_key) => _key === key)
      checkedKeys.value.checked.splice(removedIndex, 1)
    }
  }
}

const { projectId, workspace, treeToTranslate } = toRefs(useGlobalStore())
const treeData: Ref<TransformItem[]> = ref([])

const [hasAxureDoc, setHasAxureDoc] = useState(false)
const statusQueue: Ref<string[]> = ref([])
const handleProgressing = (minWait = 500) => {
  let last = Date.now()

  return (statusText: string, done: boolean) => {
    if (!statusQueue.value.length) {
      statusQueue.value.push(statusText)
    } else {
      const now = Date.now()

      // prettier-ignore
      setTimeout(() => {
        statusQueue.value.push(statusText)

        if (done) {
          setTimeout(() => {
            setHasAxureDoc(true)
          }, 800);
        }
      }, now - last < minWait ? minWait : 0)

      last = now
    }
  }
}

type Key = number | string
const checkedKeys: Ref<{
  checked: Key[]
  halfChecked: Key[]
}> = ref({ checked: [], halfChecked: [] })

/**
 * @param doc 需要获取的文件名称
 */
const pipeline = async (docName: string | null = null) => {
  try {
    // 1、获取远程 Axure 目录, 存储该远程目录数据
    const { doc, isUpdated } = await useAxureDoc(
      projectId.value,
      workspace.value,
      docName,
      handleProgressing()
    )

    // 2、初次转换为 a-tree 需要的格式的数据
    if (isUpdated) {
      treeData.value = doc as TransformItem[]
      baseWalk(doc as TransformItem[], (item) => checkedKeys.value.checked.push(item.key))
    } else {
      treeData.value = transformRemoteTree(doc as TreeItem[], (item) => {
        !item.noKey && checkedKeys.value.checked.push(item.key)
      })
    }
    // 3、增删改
  } catch (error) {
    message.error(JSON.stringify(error).replace(/(^"|"$)/g, ''), 8)
  }
}

const walk = (
  root: TransformItem[],
  parent: MenuItem | null,
  parent2: TransformItem | null,
  callback: (
    item: TransformItem,
    parent: MenuItem | null,
    parent2: TransformItem | null
  ) => [MenuItem, TransformItem]
) => {
  root.forEach((item) => {
    let nextParent: MenuItem | null = null
    let nextParent2: TransformItem | null = null

    if (item.checked) {
      ;[nextParent, nextParent2] = callback(item, parent, parent2)
    }

    if (item.children) {
      walk(item.children, nextParent, nextParent2, callback)
    }
  })
}

// 获取所有选中的数据并格式化
const collectCheckedWithFormate = () => {
  const checked: Menu = []
  // 用于读取缓存
  const originChecked: TransformItem[] = []

  const formatLabel = (label: string) => {
    return label
      .replace(/([【(（].*[）)】])/, '')
      .replace(/\//, '-')
      .replace(/\*/, '')
  }

  walk(treeData.value, null, null, (item, parent, parent2) => {
    const checkedItem: MenuItem = {
      id: item.key,
      key: item.key,
      label: formatLabel(item.title),
      title: item.title,
      url: item.url || '',
      translate: ''
    }

    const itemCopy: TransformItem = { ...item, children: void 0 }

    if (parent) {
      if (parent.children) parent.children.push(checkedItem)
      else parent.children = [checkedItem]
    } else {
      checked.push(checkedItem)
    }
    if (parent2) {
      if (parent2.children) {
        parent2.children.push(itemCopy)
      } else {
        parent2.children = [itemCopy]
      }
    } else {
      originChecked.push(itemCopy)
    }

    return [checkedItem, itemCopy]
  })

  return [checked, originChecked]
}

const [loading, setLoading] = useState(false)
const { treePath } = useGlobalStore()
const stepToNext = async () => {
  setLoading(true)

  emit('processing')

  const [menuWithoutTranslate, origialModified] = collectCheckedWithFormate()

  // 存储当前修改好的树结构数据，并进行存储
  try {
    await useWriteFile({
      root: treePath,
      filename: `updated_tree_${projectId.value}.json`,
      content: origialModified,
      useExport: false
    })
  } catch (error: any) {
    message.error(`写入更新后的数据失败: ${error?.message ?? error}`, 5)

    setLoading(false)

    return
  }

  treeToTranslate.value = menuWithoutTranslate as MenuItem[]

  setTimeout(() => {
    emit('done')
    setLoading(false)
  }, 500)
}

onDeactivated(() => {
  hasAxureDoc.value = false
  statusQueue.value = []
  treeData.value = []
  checkedKeys.value = { checked: [], halfChecked: [] }
})

const [open, setOpen] = useState(false)
const [modalWidth] = useState('max-content')
const [isQuering, setQuering] = useState(true)
const [hasRecords, setHasRecords] = useState(true)
const existFileList: { name: string; id: string }[] = reactive([])
const { updatedFile, originalFile } = useGlobalStore()
// 检查是否有历史数据
const checkEachRecordsBeforeFetch = async () => {
  try {
    // 使用 Promise.all 会有 BUG，这里会多一次请求，且返回响应时 对应的数据会重复
    // const [{ isExist: updateExist }, { isExist: originalExist }] = await Promise.all([
    //   usePathExist({
    //     path: `${treePath}\\updated_tree_${projectId.value}.json`
    //   }),
    //   usePathExist({
    //     path: `${treePath}\\original_tree_${projectId.value}.json`
    //   })
    // ])
    const last = Date.now()

    setOpen(true)

    const { isExist: updateExist } = await usePathExist({
      path: `${treePath}\\updated_tree_${projectId.value}.json`
    })

    updateExist && existFileList.push({ name: '修改后的目录数据', id: updatedFile })

    const { isExist: originalExist } = await usePathExist({
      path: `${treePath}\\original_tree_${projectId.value}.json`
    })

    originalExist && existFileList.push({ name: '初始目录数据', id: originalFile })

    if (existFileList.length) existFileList.push({ name: '重新获取', id: '' })
    else setHasRecords(false)

    await useMinWaitPromise(last, null, 800)

    if (hasRecords.value) {
      setQuering(false)
    } else {
      await useDelay(500)
      onSelectFile('')
    }
  } catch (e: any) {
    console.log(e)
    statusQueue.value.push('获取历史文件出错')
  }

  return
}

const onSelectFile = (filename: string) => {
  setOpen(false)

  setTimeout(() => {
    pipeline(filename)
  }, 350)
}

onMounted(checkEachRecordsBeforeFetch)

onActivated(checkEachRecordsBeforeFetch)
</script>

<style lang="less" scoped>
.create-menu {
  box-shadow: 0 0 40px rgba(0, 0, 0, 0.5) !important;
  &:hover {
    background-color: rgba(255, 255, 255, 0.1);
  }

  &:active {
    background-color: transparent;
  }

  &:focus-visible {
    outline: none;
  }

  &.ant-btn-loading {
    opacity: 1;
    background-color: rgba(255, 255, 255, 0.1) !important;
  }
}

.record {
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
  transition: box-shadow 0.3s ease-in-out;

  &:hover {
    box-shadow: 0 0 15px rgba(255, 255, 255, 0.05);
  }
}

:deep(.ant-modal) {
  transition: width 1s ease-in-out;
}
</style>
